import {
    ArcRotateCamera,
    BackgroundMaterial,
    Color3,
    Color4,
    ComputeShader,
    CubeTexture,
    Effect,
    Engine,
    HemisphericLight,
    Matrix,
    Mesh,
    MeshBuilder,
    MirrorTexture,
    Plane,
    RawTexture,
    Scene,
    ShaderMaterial,
    Size,
    StandardMaterial,
    Texture,
    UniformBuffer,
    Vector3,
    Vector4,
    WebGPUEngine,
} from "@babylonjs/core";
import { getDefaultMatrix } from '../component/MathLib'
import { act } from "react-dom/test-utils";
import { copyTextureToTexturePixelShader } from "@babylonjs/core/Shaders/copyTextureToTexture.fragment";

//这个是WebGPU的计算着色器 毕竟抽象
export default class SkyBox {
    public engine!: Engine;
    public scene!: Scene;
    public canvas: HTMLCanvasElement;

    private updateAction: Map<number, (time: number) => {}> = new Map();
    private updateActionId: number = 0;

    public static skyBox: SkyBox;

    public constructor(canvas: HTMLCanvasElement) {
        this.canvas = canvas;
        // this.engine = new Engine(canvas, true);
        this.createEngin().then((eng) => {
            this.engine = eng;
            this.scene = this.createScene();
            this.init();
            SkyBox.skyBox = this;
        })

    }

    private init() {
        this.update();
    }

    private async createEngin(): Promise<Engine> {
        const webGPUSupported = await WebGPUEngine.IsSupportedAsync;
        if (webGPUSupported) {
            const engine = new WebGPUEngine(this.canvas);
            await engine.initAsync();
            return engine;
        }
        return new Engine(this.canvas);
    }

    private createScene() {
        const { engine, canvas } = this;
        let scene = new Scene(engine);
        scene.clearColor = new Color4(0, 0, 0, 1);

        const camera = new ArcRotateCamera("Camera", Math.PI / 1.5, Math.PI / 3, 10, new Vector3(0, 0, 0), scene);
        camera.attachControl(canvas, true);



        console.log(engine.getCaps().supportComputeShaders)//是否支持

        const cs1 = new ComputeShader(
            'myCompute',
            engine,
            { computeSource: copyTextureToTexturePixelShader },
            {
                bindingsMapping: {
                    'pvM4': { group: 0, binding: 0 },
                    'color': { group: 1, binding: 0 },
                }
            }
        )
        const bBuffer = new UniformBuffer(engine);
        bBuffer.updateVector4('color', new Vector4(0, 1, 0, 1));
        bBuffer.updateMatrix('pvM4', getDefaultMatrix());
        bBuffer.update();

        cs1.setUniformBuffer('color', bBuffer);

        const box = MeshBuilder.CreateBox('box',);
        box.position.y = 0.5;
        const rawTexture = RawTexture.CreateRGBAStorageTexture(null, 512, 512, scene, false, false);
        const mat = new BackgroundMaterial('backmat');
        // mat.diffuseTexture = rawTexture;
        mat.useRGBColor = false;
        mat.primaryColor = new Color3(1, 0, 0);
        box.material = mat;
        //感觉这个不是很对



        const ground = MeshBuilder.CreateGround('ground', { width: 10, height: 10 });
        const mirror = new MirrorTexture('mirror', 1024);
        mirror.renderList = [box];
        mirror.mirrorPlane = new Plane(0, -1, 0, 0);
        const backMat = new BackgroundMaterial('backMat');
        backMat.reflectionTexture = mirror;
        backMat.useRGBColor = false;
        backMat.primaryColor = new Color3(1, 1, 1);
        backMat.reflectionFresnel = true;
        backMat.reflectionStandardFresnelWeight = 0.9;
        ground.material = backMat;

        return scene;
    }

    private update() {
        const { updateAction, scene } = this;

        this.engine.runRenderLoop(() => {
            updateAction.forEach((action, key) => {
                action(Date.now());
            });
            scene.render();
        })

    }

    public setUpdateAction(action: (time: number) => {}, id: number = -1) {
        const { updateAction } = this;
        let curId = id;
        if (id == -1) {
            curId = this.updateActionId++;
        } else if (id >= this.updateActionId) {
            curId = this.updateActionId++;
        }
        updateAction.set(curId, action);
    }

    public addAction(action: (time: number) => {}) {
        this.setUpdateAction(action, -1);
    }
}
